﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

using NeurOthello.Logic;
using NeurOthello.NNetwork;

namespace NeurOthello
{
    public partial class NeurOthelloMain : Form
    {
        Logic.Othello game;
        System.Threading.Thread gameThread = null;
        System.Threading.Thread trainingThread = null;        
        Othello trainingGame;
        Player trainingPlayer, player1, player2;
        
        
        string[] heuristics = new string[2] { "MinMax Neural Net", "Neural Net" };

        public NeurOthelloMain()
        {                                 
            InitializeComponent();            
        }

        private void GameUpdate(object sender, EventArgs e)
        {
            board.Invoke((MethodInvoker)delegate
            {
                board.Refresh();
            });
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            
            this.game = new Bitboard();
            trainingGame = new Bitboard();
            game.Update += new Logic.UpdateEventHandler(GameUpdate);
            board.Game = game;

            this.numNgames.IsInt = true;
            this.numTDlim.IsInt = true;
            this.numExpHL.IsInt = true;
            this.numLearnHL.IsInt = true;
            this.numTests.IsInt = true;
            this.numCycles.IsInt = true;
            
            trainingPlayer = new NeuroPlayer(trainingGame, new PlayerSettings());

            numLearning.Tag = trainingPlayer.Set.Learning;
            numLearnHL.Tag = trainingPlayer.Set.LernHL;
            numLambda.Tag = trainingPlayer.Set.Lambda;
            numExploration.Tag = trainingPlayer.Set.Exploration;
            numExpHL.Tag = trainingPlayer.Set.ExpHL;
            numTDlim.Tag = trainingPlayer.Set.TDLimit;

            player1 = new NeuroPlayer(game, new PlayerSettings());
            player1.Set.IsCpu = false;
            
            player2 = new NeuroPlayer(game, new PlayerSettings());

            this.cmbAiMethod1.Items.AddRange(heuristics);
            this.cmbAiMethod1.SelectedItem = this.cmbAiMethod1.Items[0];
            this.cmbAiMethod2.Items.AddRange(heuristics);
            this.cmbAiMethod2.SelectedItem = this.cmbAiMethod2.Items[0];                  
        }

        private void btnNewGame_Click(object sender, EventArgs e)
        {
            board.ClearStates();
            if (gameThread == null || !gameThread.IsAlive)
            {
                gameThread = new System.Threading.Thread(GameStart);
                gameThread.Start();
            }
            else
            {
                gameThread.Abort();
                gameThread = new System.Threading.Thread(GameStart);
                gameThread.Start();
            }
        }

        private void GameStart()
        { 
            if (player1.Set.IsCpu && player2.Set.IsCpu)
                game.playGame(player1, player2);      
            else if (player1.Set.IsCpu)
                game.playGame(player1, board.Human);   
            else if (player2.Set.IsCpu)
                game.playGame(board.Human, player2); 
            else
                game.playGame(board.Human, board.Human); 
        }

        private void Training()
        {        
            int nGames, cycles, nTest;
            Int32.TryParse(numNgames.Text, out nGames);
            Int32.TryParse(numCycles.Text, out cycles);
            Int32.TryParse(numTests.Text, out nTest);
            player1.SetGame(trainingGame);

            System.Console.Out.WriteLine("training in progress");
            int[] stats = new int[3], testStats = new int[3];
            for (int c = 0; c < cycles; c++)
            {
                for (int i = 0; i < nGames; i++)
                {                    
                    stats[trainingGame.playTrainingGame(trainingPlayer, trainingPlayer)]++;
                    trainingPlayer.Set.Time++;
                }

                for (int i = 0; i < nTest; i++)
                {
                    testStats[trainingGame.playGame(trainingPlayer, player1)]++;
                    testStats[(3-trainingGame.playGame(player1, trainingPlayer)) % 3]++;
                }
                System.Console.Out.WriteLine("played " + nGames + " training games, black wins: " + stats[1] + ", white wins: " + stats[2] + ", draws: " + stats[0]);
                System.Console.Out.WriteLine("played " + nGames + " test games, traine wins: " + testStats[1] + ", opponent wins: " + testStats[2] + ", draws: " + testStats[0]);
                stats[0] = 0; stats[1] = 0; stats[2] = 0; testStats[0] = 0; testStats[1] = 0; testStats[2] = 0;
                
                //((NeuroPlayer)trainingPlayer).SaveNetwork(txbFilename.Text + c * nGames);
            }
            player1.SetGame(game);

        }

        private void NeurOthello_FormClosing(object sender, FormClosingEventArgs e)
        {
           if(gameThread != null)
                gameThread.Abort();
           if (trainingThread != null)
               trainingThread.Abort();
        }

        private void btnTrain_Click(object sender, EventArgs e)
        {
            board.ClearStates();
            TrainingSettings();
            
            
            if (trainingThread != null && trainingThread.IsAlive)
                trainingThread.Abort();
                
            trainingThread = new System.Threading.Thread(Training);
            trainingThread.Start();
           
        }

        private void TrainingSettings()
        {
            trainingPlayer.Set.TDLimit = Int32.Parse(numTDlim.Text);
            trainingPlayer.Set.LernHL = Int32.Parse(numLearnHL.Text);
            trainingPlayer.Set.Learning = Double.Parse(numLearning.Text);
            trainingPlayer.Set.Exploration = Double.Parse(numExploration.Text);
            trainingPlayer.Set.ExpHL = Int32.Parse(numExpHL.Text);
            trainingPlayer.Set.Lambda = Double.Parse(numLambda.Text);
        }

        private void btnLoadp1_Click(object sender, EventArgs e)
        {            
            player1.Set.Network = Network.LoadNetwork(ChooseNet());
        }

        private void btnLoadp2_Click(object sender, EventArgs e)
        {
            player2.Set.Network = Network.LoadNetwork(ChooseNet());
        }

        private void btnLoadT_Click(object sender, EventArgs e)
        {
            trainingPlayer.Set.Network = Network.LoadNetwork(ChooseNet());
        }

        private Stream ChooseNet()
        {
            Stream netStream = null;
            OpenFileDialog openFileDialog1 = new OpenFileDialog();

            openFileDialog1.InitialDirectory = @"\NeurOthello\SavedNetworks\";  
            
            openFileDialog1.Filter = "nnt files (*.nnt)|*.nnt";
            openFileDialog1.FilterIndex = 2;           
            openFileDialog1.RestoreDirectory = false;// true;

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                try
                {                    
                    netStream = openFileDialog1.OpenFile();                    
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }
            openFileDialog1.Dispose();
            return netStream;
        }

        private void btnNewTrainingNet_Click(object sender, EventArgs e)
        {
            trainingPlayer.Set.Network = new Network(Int32.Parse(numHidden.Text));
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            Network.SaveNetwork(txbFilename.Text+".nnt", trainingPlayer.Set.Network);
        }

        private void cbxAi1_CheckedChanged(object sender, EventArgs e)
        {
            if (cbxAi1.Checked)
                player1.Set.IsCpu = true;
            else
                player1.Set.IsCpu = false;
        }

        private void cbxAi2_CheckedChanged(object sender, EventArgs e)
        {
            if (cbxAi2.Checked)
                player2.Set.IsCpu = true;
            else
                player2.Set.IsCpu = false;
        }

        private void numDepth1_ValueChanged(object sender, EventArgs e)
        {
            player1.Set.Depth = (int)numDepth1.Value;
        }

        private void numDepth2_ValueChanged(object sender, EventArgs e)
        {
            player2.Set.Depth = (int)numDepth2.Value;
        }

        private void cmbAiMethod1_SelectedItemChanged(object sender, EventArgs e)
        {
            if (cmbAiMethod1.SelectedIndex == 0)
            {
                Player tmp = new MiniMax(game, player1.Set);
                player1 = tmp;
            }
            else if (cmbAiMethod1.SelectedIndex == 1)
                player1 = new NeuroPlayer(game, player1.Set);
        }

        private void cmbAiMethod2_SelectedItemChanged(object sender, EventArgs e)
        {
            if (cmbAiMethod2.SelectedIndex == 0)
                player2 = new MiniMax(game, player2.Set);
            else if (cmbAiMethod2.SelectedIndex == 1)
                player2 = new NeuroPlayer(game, player2.Set);
        }

        private void btnTest_Click(object sender, EventArgs e)
        {

        } 
    }
}


/*
			#region PlayerSetup	
				if (cbxAi1.Checked)
				{
					Heuristic heur;
					switch (cmbAiHeuristic1.SelectedIndex)
					{
						case 1:
							heur = new Logic.NNHeuristic("nyanetwerketplusturn332000.nnt");
							break;
						default:
							heur = new Heuristic(1, 1, 1);
							break;
					}
					p1 = new MiniMax(game, heur, (int)numDepth1.Value);
				}
				else
					p1 = board.Human;

				if (cbxAi2.Checked)
				{
					Heuristic heur;
					switch (cmbAiHeuristic2.SelectedIndex)
					{
						case 1:
							heur = new Logic.NNHeuristic("nyanetwerketplusturn332000.nnt");
							break;
						default:
							heur = new Heuristic(1, 1, 1);
							break;
					}
					p2 = new MiniMax(game, heur, (int)numDepth2.Value);
				}
				else
					p2 = board.Human;  
			#endregion



 * 
 * #region playerSetup
            PlayerSettings set = new PlayerSettings();
            int tmpI;
            Int32.TryParse(numTDlim.Text, out tmpI);
            set.TDLimit = tmpI;

            Int32.TryParse(numExpHL.Text, out tmpI);
            set.ExpHL = tmpI;

            Int32.TryParse(numLearnHL.Text, out tmpI);
            set.LernHL = tmpI;

            double tmp;
            Double.TryParse(numLearning.Text, out tmp);
            set.Learning = tmp;

            Double.TryParse(numExploration.Text, out tmp);
            set.Exploration = tmp;

            Double.TryParse(numLambda.Text, out tmp);
            set.Lambda = tmp;

            set.SelfTraining = chbSelf.Checked;
            set.Time = 0;

            trainingPlayer.Set = set;

            Heuristic heur;
			switch (cmbAiHeuristic1.SelectedIndex)
			{
				case 1:
                    heur = new Logic.NNHeuristic("nyanetwerketplusturn2103000.nnt");
					break;
				default:
					heur = new Heuristic(1, 1, 1);
					break;
			}
			//Player = new MiniMax(trainingGame, heur, (int)numDepth1.Value);

            #endregion




*/